/*
 * Created by Ben Kahn on 3/9/09
 */

package client.engine;

import java.awt.Component;
import java.awt.Toolkit;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;

import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JOptionPane;

import server.BuddyList;
import server.Group;
import server.Person;
import server.Preferences;
import server.Status;
import server.Preferences.ConversationWindow;
import client.ui.IBuddyListWindow;
import client.ui.IChatRoomTab;
import client.ui.IChatRoomWindow;
import client.ui.IConversationHistoryWindow;
import client.ui.ILoginWindow;
import client.ui.IPreferencesWindow;
import client.ui.ITheme;
import client.ui.ThemeFactory;
import client.ui.ThemeName;

import communication.FileAcceptorConnection;

public class ChatEngine {

	private RequestProducer producer;
	private RequestConsumer consumer;
	
	private boolean online;
	private KeepAlive alive;
	private boolean receivedBuddyPreferences;
	private boolean receivedErrorMessage;
	
	private String myUsername;
	private BuddyList myBuddies;
	private Preferences myPrefs;
	
	private HashMap<String, ChatRoom> imChatRooms;
	private ITheme theme = null;
	private ThemeFactory factory = null;
	private ArrayList<IChatRoomWindow> chatRoomWindows = null;
	private IBuddyListWindow buddyListWindow;
	private ILoginWindow login;
	
	
	public ChatEngine() {
		myUsername = "";
		online = false;
		init();
	}
	
	public void init()
	{
		imChatRooms = new HashMap<String, ChatRoom>();
		producer = new RequestProducer();
		consumer = new RequestConsumer(this);
		consumer.start();
		factory = new ThemeFactory();
		theme = factory.createTheme(ThemeName.DEFAULT_THEME);
		buddyListWindow = theme.createBuddyListWindow();
		buddyListWindow.setChatEngine(this);
		chatRoomWindows = new ArrayList<IChatRoomWindow>();
		login = theme.createLoginWindow();
		login.setChatEngine(this);
		launchLoginFrame();
	}
	
	public void launchLoginFrame()
	{
		((JFrame)login).setVisible(true);
	}
	
	public void launchChatRoomWindow()
	{
		//((JFrame) chatRoomWindow).setVisible(true);
	}
	
	public RequestProducer getProducer() {
		return producer;
	}
	
	public RequestConsumer getConsumer() {
		return consumer;
	}

	public void setUsername(String username)
	{
		myUsername = username;
	}
	
	public String getUsername()
	{
		return myUsername;
	}
	
	public boolean isOnline()
	{
		return online;
	}
	
	public void removeConversation(String convID){
		
		this.imChatRooms.remove(convID);
		producer.sendAbandonConversationRequest(myUsername, convID);
		
	}
	
	public void keepMeAlive()
	{
		alive = new KeepAlive(this);
		alive.start();
	}
	/**************************************************
	 * ===============================================*
	 * THIS IS WHERE OUTGOING INFORMATION IS PROCESSED*
	 * ===============================================*
	 *************************************************/
	public void signOn(String username, String password)
	{
		producer.sendSignOnRequest(username, password);
		//System.out.println(producer.getLocalHost());
		//login.dispose();
	}
	
	public void signOff()
	{
		for(int i = 0; i < chatRoomWindows.size(); i++){
			IChatRoomWindow window = chatRoomWindows.get(i);
			window.closeWindow();
		}
		producer.sendSignOffRequest(this.myUsername);
	}
	
	public void createAccount(String username, String password)
	{
		producer.sendNewAccountRequest(username, password);
	}
	
	public void sendMessage(String destination, String message)
	{
		System.out.println(myUsername + " " + destination + " " + message);
		producer.sendSendMessageRequest(this.myUsername, destination, message);
	}
	
	public void stillOnline()
	{
		producer.sendStillOnlineRequest(myUsername);
	}
	
	public void away(String message)
	{
		throw new UnsupportedOperationException();
	}
	
	public void openConversation(String buddy)
	{
		producer.sendOpenConversationRequest(myUsername, buddy);
		
		if(myPrefs.getConvWindow() == ConversationWindow.TABBED){
			
			if(chatRoomWindows.size() == 0){
				IChatRoomWindow window = theme.createChatRoomWindow();
				window.setChatEngine(this);
				chatRoomWindows.add(window);
				window.showWindow();
			}
		}
	}
	
	/**************************************************
	 * ===============================================*
	 * THIS IS WHERE INCOMING INFORMATION IS PROCESSED*
	 * ===============================================*
	 *************************************************/
	public void receiveMessage(String conversationID, String sender, String message)
	{
		if(myPrefs.isSoundActive()){
			Toolkit.getDefaultToolkit().beep(); 
		}
		imChatRooms.get(conversationID).receiveMessage(sender, message);
	}
	
	public void setBuddyList(BuddyList bl)
	{
		if(online)
		{
			this.myBuddies = bl;
			buddyListWindow.displayBuddyList(bl);
		}
		else
		{
			((JFrame) login).dispose();
			
			((JFrame) buddyListWindow).setVisible(true);
			this.myBuddies = bl;
			buddyListWindow.displayBuddyList(bl);
			online = true;
		}
		
	}
	
	public BuddyList getBuddyList(){
		
		return myBuddies;
		
	}
	
	public void setPreferences(Preferences p){
		
		myPrefs = p;
		
	}
	
	public Preferences getPreferences(){
		
		return myPrefs;
		
	}
	
	public void conversationOpened(ArrayList<String> buddies, String id)
	{
		ArrayList<Person> participants = new ArrayList<Person>();
		for(int i = 0; i < buddies.size(); i++){
			Person p = new Person(buddies.get(i));
			participants.add(p);
		}
		
		IChatRoomTab tab = theme.createChatRoomTab();
		tab.setChatEngine(this);
		
		BufferedWriter log = null;
		
		try{
			FileWriter file = new FileWriter(myPrefs.getDefaultPath() + id + System.currentTimeMillis() + ".par");
			log = new BufferedWriter(file);
		}catch(Exception e){
			
		}
		
		ChatRoom room = new ChatRoom(this, id, participants, tab, log);
		tab.setChatRoom(room);
		
		this.imChatRooms.put(id, room);
		
		if(myPrefs.getConvWindow() == ConversationWindow.INDIVIDUAL){
			IChatRoomWindow window = theme.createChatRoomWindow();
			window.setChatEngine(this);
			chatRoomWindows.add(window);
			window.showWindow();
			window.addTab(tab);
			tab.setChatRoomWindow(window);
		}else{
			if(chatRoomWindows.size() == 0){
				IChatRoomWindow window = theme.createChatRoomWindow();
				window.setChatEngine(this);
				chatRoomWindows.add(window);
				window.showWindow();
			}
			chatRoomWindows.get(0).addTab(tab);
			tab.setChatRoomWindow(chatRoomWindows.get(0));
		}
		
	}
	
	public void errorFromServer(String message)
	{
		handleServerMessage(message);
		login.setWaitingState(true);
	}
	
	
	
	public void addParticipantToConversation(String buddy, String id)
	{
		ChatRoom room = this.imChatRooms.get(id);
		
		if(room != null){
			room.addParticipant(new Person(buddy));
		}
	}
	
	public void removeParticipantFromConversation(String buddy, String id)
	{
		ChatRoom room = this.imChatRooms.get(id);
		
		if(room != null){
			room.removeParticipant(new Person(buddy));
		}
	}
	
	public void changeBuddyStatus(String buddy, Status s)
	{
		throw new UnsupportedOperationException();
	}
	
	
	
	public void handleServerMessage(String message)
	{	
		login.clearWindow();
		login.showError(message);
	}
	
	public void handleBuddyStatusChange(String buddy, Status newStatus)
	{
		Iterator<Group> itgroup = myBuddies.getGroups();
		while(itgroup.hasNext()){
			Group g = itgroup.next();
			Iterator<Person> it = g.getMembers();
			while(it.hasNext()){
				Person p = it.next();
				if(p.getUsername().equals(buddy)){
					p.setStatus(newStatus);
				}
			}
		}
		
		this.buddyListWindow.displayBuddyList(myBuddies);
	}
	
	public void handleConfirmNewBuddy(String buddy){
		
		int response = JOptionPane.showConfirmDialog((Component) this.buddyListWindow, "Do you want to confirm " + buddy + " as your contact?",
				"Confirm buddy", JOptionPane.YES_NO_OPTION);
		
		if(response == JOptionPane.YES_OPTION){
			String groupName = null;
			do{
				groupName = JOptionPane.showInputDialog("Insert group in which you want to add the new buddy:");
				
				if(myBuddies.findGroup(groupName) == null){
					this.createGroup(groupName);
				}
			}while(myBuddies.findGroup(groupName) == null);
			producer.sendAcceptNewBuddyRequest(myUsername, groupName, buddy);
		}else{
			producer.sendRejectNewBuddyRequest(myUsername, buddy);
		}
	}
	
	public void abandonConversation(String id){
		
		this.imChatRooms.remove(id);
		producer.sendAbandonConversationRequest(myUsername, id);
		
	}
	
	public void inviteBuddyToConversation(String convID, String name){
		
		producer.sendInviteBuddyChatRoomRequest(myUsername, name, convID);
		
	}
	
	public void createGroup(String name){
		
		Group g = new Group(name);
		myBuddies.addGroup(g);
		this.buddyListWindow.displayBuddyList(myBuddies);
		
		producer.sendAddNewGroupRequest(myUsername, name);
		
	}
	
	public boolean removeGroup(String name){
		
		Group g = myBuddies.findGroup(name);
		
		if(g != null){
			if(myBuddies.removeGroup(g)){
				producer.sendRemoveGroupRequest(myUsername, name);
				buddyListWindow.displayBuddyList(myBuddies);
				return true;
			}else{
				return false;
			}
		}else{
			return false;
		}
		
	}
	
	public void addBuddy(String buddy, String group){
		
		Group g = myBuddies.findGroup(group);
		if(g.findPerson(buddy) == null){
			g.addMember(new Person(buddy));
			buddyListWindow.displayBuddyList(myBuddies);
			producer.sendAddNewBuddyRequest(myUsername, group, buddy);
		}
		
	}
	
	public void removeBuddy(String buddy){
		
		Iterator<Group> itgroup = myBuddies.getGroups();
		
		while(itgroup.hasNext()){
			Group g = itgroup.next();
			Person p = g.findPerson(buddy);
			if(p != null){
				producer.sendRemoveBuddyFromGroupRequest(myUsername, g.getName(), buddy);
				g.removeMember(p);
			}
		}
		
		buddyListWindow.displayBuddyList(myBuddies);
	}
	
	public void storePreferences(Preferences pref){
		
		this.myPrefs = pref;
		
		this.producer.sendStoreNewPreferencesRequest(myUsername, myPrefs);
		this.buddyListWindow.displayBuddyList(myBuddies);
		
	}
	
	public void showPreferencesWindow(){
		
		IPreferencesWindow window = theme.createPreferencesWindow();
		
		window.setChatEngine(this);
		window.showWindow();
		window.displayPreferences(myPrefs);
		
	}
	
	public void showHistory(){
		
		IConversationHistoryWindow window = theme.createConversationHistoryWindow();
		window.setChatEngine(this);
		window.showWindow();
		window.displayConversationHistory(myPrefs.getDefaultPath());
		
	}
	
	public void closeWindow(IChatRoomWindow w){
		
		this.chatRoomWindows.remove(w);
		
	}
	
	public void sendFile(String receiver, String path, String name){
		
		producer.sendFileTransfer(myUsername, receiver, path, name);
		
	}
	
	public void handleConfirmFileTransfer(String sender, String receiver, String path, String file, InetAddress ip, int port){
		
		int response = JOptionPane.showConfirmDialog((Component) buddyListWindow, "Do you want to accept file " + file + " from " + sender + "?", "Confirm file transfer", JOptionPane.YES_NO_OPTION);
		
		if(response == JOptionPane.YES_OPTION){
			JFileChooser saveChooser = new JFileChooser();
			
			try{
				int result = saveChooser.showSaveDialog((Component) buddyListWindow);
				
				if(result == JFileChooser.APPROVE_OPTION){
					File f = saveChooser.getSelectedFile();
					final String recpath = f.getPath();
					final int recport = 55555;
					
					System.out.println("I accepted file " + file + " and I'm storing it in " + recpath);
					
					Runnable fileAcceptor = new Runnable(){

						@Override
						public void run() {
							try{
								System.out.println("I'm waiting to receive a file");
								FileAcceptorConnection fac = new FileAcceptorConnection(recport);
								fac.acceptFile(recpath);
								System.out.println("File received");
								JOptionPane.showMessageDialog(null, "File recieved", "File Confirmation", 1);
							}catch(Exception e){
								
							}
						}
						
					};
					Thread transfer = new Thread(fileAcceptor);
					transfer.start();
					
					producer.confirmFileTransfer(sender, receiver, path, path, InetAddress.getLocalHost(), recport);
				}
			}catch(Exception e){
				
			}
		}else{
			
		}
		
	}
}


